#include "Config.h"
#include "Utils.h"
#include "StlFile.h"
#include <exception>
#include <rapidjson\document.h>
#include <rapidjson\filestream.h>

namespace GPUCC
{
	Config::Config() : m_SignaturesFolder(Utils::GetFullPath(".\\db\\")), m_GPUMaxFileSize(1000 * 1024), m_MaxFileSize(0) 
	{
	}

	Config::~Config()
	{
	}

	void Config::Load(const std::string& Path)
	{
		StlFile configFile(Path);

		if (!configFile.Open(true))
		{
			throw std::exception("Error opening configuration file.");
		}
		else
		{
			std::string jsonValue((std::istreambuf_iterator<char>(configFile.GetStream())), std::istreambuf_iterator<char>());

			configFile.Close();

			this->Parse(jsonValue);
			
			this->Validate();
		}
	}

	void Config::Parse(const std::string& Value)
	{
		rapidjson::Document jsonDoc;

		if (jsonDoc.Parse<rapidjson::kParseDefaultFlags>(Value.c_str()).HasParseError())
		{
			throw std::exception("Error parsing json.");
		}
		else if (!jsonDoc.IsObject())
		{
			throw std::exception("Root json element is not an object.");
		}
		else if ((!jsonDoc.HasMember("signatures_folder")) || (!jsonDoc["signatures_folder"].IsString()))
		{
			throw std::exception("Error with signatures_folder member.");
		}
		else if ((!jsonDoc.HasMember("max_file_size")) || (!jsonDoc["max_file_size"].IsUint()))
		{
			throw std::exception("Error with max_file_size member.");
		}
		else if ((!jsonDoc.HasMember("gpu_max_file_size")) || (!jsonDoc["gpu_max_file_size"].IsUint()))
		{
			throw std::exception("Error with gpu_max_file_size member.");
		}
		else if ((!jsonDoc.HasMember("platform_name")) || (!jsonDoc["platform_name"].IsString()))
		{
			throw std::exception("Error with platform_name member.");
		}
		else if ((!jsonDoc.HasMember("gpu_name")) || (!jsonDoc["gpu_name"].IsString()))
		{
			throw std::exception("Error with gpu_name member.");
		}
		else if ((!jsonDoc.HasMember("max_number_of_gpu_groups")) || (!jsonDoc["max_number_of_gpu_groups"].IsUint()))
		{
			throw std::exception("Error with max_number_of_gpu_groups member.");
		}
		else if ((!jsonDoc.HasMember("max_size_of_gpu_group")) || (!jsonDoc["max_size_of_gpu_group"].IsUint()))
		{
			throw std::exception("Error with max_size_of_gpu_group member.");
		}
		else if ((!jsonDoc.HasMember("max_file_size_without_streaming")) || (!jsonDoc["max_file_size_without_streaming"].IsUint()))
		{
			throw std::exception("Error with max_file_size_without_streaming member.");
		}
			else if ((!jsonDoc.HasMember("max_number_of_stream_groups")) || (!jsonDoc["max_number_of_stream_groups"].IsUint()))
		{
			throw std::exception("Error with max_number_of_stream_groups member.");
		}
		else if ((!jsonDoc.HasMember("max_size_of_stream_group")) || (!jsonDoc["max_size_of_stream_group"].IsUint()))
		{
			throw std::exception("Error with max_size_of_stream_group member.");
		}

		this->m_SignaturesFolder = Utils::GetFullPath(jsonDoc["signatures_folder"].GetString());
		this->m_MaxFileSize = jsonDoc["max_file_size"].GetUint();
		this->m_GPUMaxFileSize = jsonDoc["gpu_max_file_size"].GetUint();
		this->m_PlatformName = jsonDoc["platform_name"].GetString();
		this->m_GPUName = jsonDoc["gpu_name"].GetString();
		this->m_MaxNumberOfGroups = jsonDoc["max_number_of_gpu_groups"].GetUint();
		this->m_MaxSizeOfGroup = jsonDoc["max_size_of_gpu_group"].GetUint();
		this->m_MaxFileSizeWithoutStreaming = jsonDoc["max_file_size_without_streaming"].GetUint();
		this->m_MaxNumberOfStreamGroups = jsonDoc["max_number_of_stream_groups"].GetUint();
		this->m_MaxSizeOfStreamGroup = jsonDoc["max_size_of_stream_group"].GetUint();
	}

	void Config::Validate()
	{
		if (!Utils::DirectoryExists(this->m_SignaturesFolder))
		{
			throw std::exception("Signatures folder doesn't exists!");
		}
		else if (this->m_GPUMaxFileSize == 0)
		{
			throw std::exception("No max file size limit for GPU scan!");
		}
		else if (this->m_GPUMaxFileSize > this->m_MaxFileSize)
		{
			throw std::exception("GPU max file size limit is larger than the maximum!");
		}
		else if (this->m_MaxNumberOfGroups == 0)
		{
			throw std::exception("GPU max number of groups is zero!");
		}
		else if (this->m_MaxSizeOfGroup == 0)
		{
			throw std::exception("GPU max group size is zero!");
		}
		else if (this->m_MaxFileSizeWithoutStreaming > this->m_GPUMaxFileSize)
		{
			throw std::exception("GPU max file size without streaming is larger than GPU max file size!");
		}
		else if ((this->m_MaxNumberOfStreamGroups != 0) && (this->m_MaxSizeOfStreamGroup == 0))
		{
			throw std::exception("GPU max size of stream group is zero!");
		}
	}

	std::string Config::GetSignaturesFolder() const
	{
		return (this->m_SignaturesFolder);
	}

	unsigned int Config::GetGPUMaxFileSize() const
	{
		return (this->m_GPUMaxFileSize);
	}

	bool Config::IsMaxFileSizeLimit() const
	{
		return (this->m_MaxFileSize != 0);
	}

	unsigned int Config::GetMaxFileSize() const
	{
		return (this->m_MaxFileSize);
	}

	std::string Config::GetPlatformName() const
	{
		return (this->m_PlatformName);
	}

	std::string Config::GetGPUName() const
	{
		return (this->m_GPUName);
	}

	unsigned int Config::GetMaxNumberOfGroups() const
	{
		return (this->m_MaxNumberOfGroups);
	}
	
	unsigned int Config::GetMaxSizeOfGroup() const
	{
		return (this->m_MaxSizeOfGroup);
	}

	unsigned int Config::GetMaxFileSizeWithoutStreaming() const
	{
		return (this->m_MaxFileSizeWithoutStreaming);
	}

	unsigned int Config::GetMaxNumberOfStreamGroups() const
	{
		return (this->m_MaxNumberOfStreamGroups);
	}

	unsigned int Config::GetMaxSizeOfStreamGroup() const
	{
		return (this->m_MaxSizeOfStreamGroup);
	}
}